花了一点时间读以太坊的源代码,内容太多,所以一边看一边写,没想到越写越多,于是想着把笔记整理出来。
源码解读是一件费力不讨好的事情,因为看代码的时候我们看到的是最终解决方案,虽然可以看到 commit 的历史,但我们看不到作者的思路,踩过的坑,而且 geth 的设计文档,功能的相关讨论等信息不像 Kubernetes 那么规范透明,所以我看这些代码的时候很多地方也只是一知半解,欢迎有更多经验的朋友批评指正,不吝赐教,也欢迎有价值的讨论。看了一些书和文章,所以我这也算不上多原创,基本上所有看过的链接都记在 reference 里面了。有的代码暂时看不懂就只能靠猜了,等待之后的实践中验证想法。尽管如此,对于同样想要阅读 geth 的源代码的人来说总还是有些作用,对于我个人来说整理出来这些文章也是一个总结知识,把点连成面的过程,希望同时能帮到其他人。
geth 版本:master 分支,a1eb9c7d13240fd250866219a502d0cdc9924e06
这是第一篇,可以作为索引,后续发布其他文章后会不断更新👇
- go-ethereum 源码笔记(概览)
- go-ethereum 源码笔记(基础知识)
- go-ethereum 源码笔记(cmd 模块-geth 命令)
- go-ethereum 源码笔记(cmd 模块-其他命令)
- go-ethereum 源码笔记(core 模块-区块链操作)
- go-ethereum 源码笔记(rlp 模块-序列化与反序列化)
- go-ethereum 源码笔记(trie 模块-MPT的实现)
- go-ethereum 源码笔记(accounts, transaction 模块-账户和转账)
- go-ethereum 源码笔记(core 模块-交易池模块)
- go-ethereum 源码笔记(miner,consensus 模块-挖矿和共识算法)
- go-ethereum 源码笔记(ethdb 模块)
- go-ethereum 源码笔记(core 模块-世界状态,交易收据管理)
- go-ethereum 源码笔记(core 模块-EVM-虚拟机的实现)
- go-ethereum 源码笔记(core, eth 模块-链的索引,搜索)
涉及到的计算机专业知识
- 网络知识,p2p 网络(Kad 算法) ,rpc 等等
- 加密学,暂时不需要很深入,除非是像 How to build blockchain from scratch? 说的需要构建加密函数相关的库,在大多数的场景下只需要知道加密函数的基本原理和使用。当然越深入越好,这一块也可以做出创新;安全对于很多行业是命脉,对于区块链来说更是如此。推荐阅读密码学入门经典:图解密码技术
- 编译原理,看 EVM 相关的实现时需要知道一些基本概念。
- 分布式系统原理(一致性算法)
- 智能合约
- 数据库(LevelDB),了解 LSM 的特点,不需要太深入的了解,知道 API 调用即可。当然知道底层原理最好,这块也有改进的空间,微博上的邓草原同学在做这方面的工作,针对区块链的数据特点设计专门的存储引擎,TPS 有不小的提升,不过我还没有细看代码,从讨论来看应该是参考了 Kafka 的存储特点。
- 一些数据结构
- MPT
- DAG
- 布隆过滤器
- …
写完了这个系列可能会加个思维导图。
架构
总体架构图(来自 Ethereum block architecture)
分层架构
以太坊技术详解与实战这本书里介绍了以太坊的分层架构,其中的图片还挺有参考价值的,网络上没有找到原图,所以我自己画了一下:
底层服务
底层服务包括 P2P 网络、LevelDB 数据库、密码学算法和分片优化等基础服务。
核心层
核心层包括区块链、共识算法和以太坊虚拟机等。
顶层应用
这一层包括 API 接口、智能合约以及去中心化应用。
重要的数据结构
/core/types/block.go
区块的数据结构
区块的数据结构在 core/types/block.go
中定义。先混个眼熟吧。
1 | type Block struct { |
重要字段 | 描述 |
---|---|
header | header 指向 Header 结构(之后会详细说明),header 存储一个区块的基本信息。 |
uncles | 指向 Header 结构 |
transactions | 一组 transaction 结构 |
hash | 当前区块的哈希值 |
size | 当前区块的大小 |
td | 当前区块高度 |
ReceivedAt | 接收时间 |
ReceivedFrom | 来源 |
交易组成区块,一个一个区块以单向链表的形式连在一起组成区块链,毋庸置疑,这是最基础的数据结构,在 geth 的源代码中大量用到。
其中 Header
的数据结构定义为:
1 | type Header struct { |
字段 | 描述 |
---|---|
ParentHash | 父区块的哈希值 |
UncleHash | 叔区块的哈希值 |
Coinbase | 矿工得到奖励的账户,一般是矿工本地第一个账户 |
Root | 表示当前所有用户状态 |
TxHash | 本区块所有交易 Hash,即摘要 |
ReceiptHash | 本区块所有收据 Hash,即摘要 |
Bloom | 布隆过滤器,用来搜索收据 |
Difficulty | 该区块难度,动态调整,与父区块和本区块挖矿时间有关。可参考 github.com/ethereum/go-ethereum/consensus/ethash/consensus.go 的 CalcDifficulty |
Number | 该区块高度 |
GasLimit | gas 用量上限,该数值根据父区块 gas 用量调节,如果 parentGasUsed > parentGasLimit * (2/3) ,则增大该数值,反之则减小该数值。可参看 github.com/ethereum/go-ethereum/core/block_validator.go 的 CalcGasLimit |
GasUsed | 实际花费的 gas |
Time | 新区块的出块时间,严格来说是开始挖矿的时间 |
Extra | 额外数据 |
MixDigest | 混合哈希,与 nonce 结合使用 |
Nonce | 加密学中的概念,在基本概念章节中有介绍 |
它包含区块的属性信息,ParentHash
表示该区块的父区块哈希,我们通过 ParentHash
这个字段将一个一个区块连接起来组成区块链,但实际上我们并不会直接将链整个的存起来,它是以一定的数据结构一块一块存放的,geth 的底层数据库用的是 LevelDB,这是一个 key-value 数据库,要得到父区块时,我们通过 ParentHash
以及其他字符串组成 key,在 LevelDB 中查询该 key 对应的值,就能拿到父区块。
/core/blockchain.go 区块链的数据结构
core/blockchain.go
的 BlockChain
结构体定义了区块链的数据结构
1 | type BlockChain struct { |
UncleHash
是 Block
结构体成员 uncles
的 RLP 哈希值,uncles
是一个 Headers
数组,关于叔区块,可以查阅以太坊的设计原理。在下一篇 go-ethereum 源码笔记(基础知识) 会详细介绍叔区块。
Block 对象中还有一个比较重要的数据结构,那就是 Transaction
,它是交易相关逻辑的基础。
/core/types/transaction.go
交易的数据结构
交易的数据结构
1 | type Transaction struct { |
转账的定义中只有转入方,转出方的地址没有直接暴露。每一笔转账都有独立的 Price 和 GasLimit,这是 Ethereum 的安全保护策略,是一个值得称赞的设计,如果你对这个不熟悉,请查阅:wiki/Glossary,在之后的文章中也会有介绍。
geth 目录结构简述
1 | accounts 实现以太坊账户管理 |
从编译源代码开始
按照官方文档的建议设置好开发环境,在 go-ethereum 执行 make build
之后,我们可以在 build/bin 目录下找到 abigen, ethkey 等等可执行文件,实际上这些文件的入口函数都在 cmd 目录下,接下来的文章里我们会逐一介绍这些命令。
参考资料
- bitcoin developer documentation
- 以太坊工作原理概述
- go-ethereum 源码解读(一)
- ethereum 黄皮书
- Ethereum Frontier Guide
- go-ethereum-code-analysis
- libp2p/specs
- go ethereum 目录介绍
- 以太坊技术详解与实战
- 图解密码技术
- 以太坊设计原理 ,非常重要,建议多读几遍
- ethereum/pydevp2p
- Jeiwan/blockchain_go
- Ethereum block architecture
- Ethereum reddit
- How does ethereum work anyway